/*
 * IPTVPlayer
 * 
 * version 0.1
 * 
 * 2013/4/1 10:14
 * 
 * Pang Haiyan
 * 
 * xiangshan network
 */

package com.iptv.player;

import java.util.Timer;
import java.util.TimerTask;

import android.graphics.Canvas;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;

/*import com.iptv.common.Common;
import com.iptv.common.IPTVConfig;
import com.iptv.common.Utils;
import com.iptv.epg.Inform;*/

enum playStatus {
	beforePlay, Play, Stop, Pause, FastForward, FastRewind
}

public class IPTVPlayer {
	public final static String LOG_TAG = "IPTVPlayer";
	public final static int StartPlay = 0x3201;
	public final static int StopPlay = 0x3202;
	public final static int FastForward = 0x3203;
	public final static int FastRewind = 0x3204;
	public final static int SeekTo = 0x3205;
	public final static int LiveSeekTo = 0x3206;
	public final static int PausePlay = 0x3207;
	public final static int Resume = 0x3208;
	public final static int StopFast = 0x3209;
	public final static int SetLive = 0x3210;
	public final static int SetProgramUrl = 0x3211;
	public final static int TimeShiftPause = 0x3212;
	public final static int INIT = 0x3213;
	public final static int RELEASE = 0x3214;

	public final static int SET_SRC_WH = 0x3215;
	public final static int SET_VIDEO_LOC = 0x3216;
	public final static int SET_AUDIO_BL = 0x3217;
	public final static int UPDATE_SURFACE_MTK = 0x3218;
	public final static int SET_SURFACE = 0x3219;

	private static IPTVPlayer iptvPlayer = null;
	public static Handler playerHandler = null;
	private HandlerThread playerHandlerThread = null;

	private playStatus status;
	private static int speed; // play speed . 1:normal play. positive:
								// fastforward; negtive : fastRewind
	private static int scrWidth;
	private static int scrHeight;
	private static boolean isLive;
	private static boolean activityPaused = false;

	private static String programUrl;

	static {
		// load library
		System.loadLibrary("CTC_MediaProcessor");
		System.loadLibrary("live555");
		System.loadLibrary("netsdk");
		System.loadLibrary("rtspclt");
	}

	public static IPTVPlayer Instance() {
		if (iptvPlayer == null) {
			iptvPlayer = new IPTVPlayer();
		}
		return iptvPlayer;
	}

	private IPTVPlayer() {
		speed = 1;
		// status = playStatus.beforePlay;
		status = playStatus.Stop;
		scrWidth = 1280;
		scrHeight = 720;
		isLive = false;
		// Log.v(LOG_TAG, Utils._FUNC_() + "isLive: " + isLive);

		getSystemWidth();

		initHandlerAndNewThread();
	}

	private void initHandlerAndNewThread() {

		Log.v(LOG_TAG,
				"--------------------java path"
						+ System.getProperty("java.library.path"));
		// 新建HandlerThread，初始化Handler
		playerHandlerThread = new HandlerThread("PlayerHandlerThread");
		playerHandlerThread.start();

		playerHandler = new Handler(playerHandlerThread.getLooper()) {
			@Override
			public void handleMessage(Message msg) {
				Log.i(LOG_TAG, "handle message: " + msg.what);
				switch (msg.what) {
				case IPTVPlayer.StartPlay:
					Log.i(LOG_TAG, "handle message: IPTVPlayer.StartPlay");
					// status == playStatus.beforePlay ||
					if (status != playStatus.Stop) {
						speed = 1;
						nativeStop();
					}
					
					nativeStop();//gy new add
					
					Log.i(LOG_TAG, "start play > programUrl = " + programUrl);
					status = playStatus.Play;
					nativeStartPlay(programUrl);
					int volume = nativeGetVolume();
					Log.i(LOG_TAG, "volume = " + volume);
					if (volume >= 0 && volume <= 100)
						nativeSetVolume(volume);
					else if (volume < 0) {
						nativeSetVolume(0);
					}

					break;
				case IPTVPlayer.StopPlay:
					Log.i(LOG_TAG, "handle message: IPTVPlayer.StopPlay");
					// status != playStatus.beforePlay &&
					
					if (status != playStatus.Stop) {
						status = playStatus.Stop;
						speed = 1;
						nativeStop();
						Log.i(LOG_TAG, "handle message: IPTVPlayer.StopPlay > nativeStop");
					}
					break;
				case IPTVPlayer.FastForward:
					Log.i(LOG_TAG, "handle message: IPTVPlayer.FastForward");
					if (status == playStatus.Play
							|| status == playStatus.FastForward) {
						status = playStatus.FastForward;
						if (speed < 64) {
							speed = speed * 2;
						} else {
							speed = 2;
						}
						Log.v(LOG_TAG, "fastForward: " + speed);
						nativeFastForward(speed);
					} else if (status == playStatus.FastRewind) {
						status = playStatus.FastForward;
						speed = 2;
						Log.v(LOG_TAG, "fastForward: " + speed);
						nativeFastForward(speed);
					}
					break;
				case IPTVPlayer.FastRewind:
					Log.i(LOG_TAG, "handle message: IPTVPlayer.FastRewind");
					if (isLive) {
//						Inform.sendMessage(Common.MSG_ADD_SHIYIIMG);
					}
					if (status == playStatus.Play
							|| status == playStatus.FastRewind) {
						status = playStatus.FastRewind;
						if (speed > -64) {
							speed = Math.abs(speed) * -2;
						} else {
							speed = -2;
						}
						Log.v(LOG_TAG, "Hai call fastRewind " + speed);
						nativeFastRewind(speed);
					} else if (status == playStatus.FastForward) {
						status = playStatus.FastRewind;
						speed = -2;
						Log.v(LOG_TAG, "Hai call fastForward " + speed);
						nativeFastRewind(speed);
					}
					break;
				case IPTVPlayer.SeekTo:
					Log.i(LOG_TAG, "handle message: IPTVPlayer.SeekTo");
					status = playStatus.Play;
					Long s = (Long) msg.obj;
					int time = s.intValue();
					nativeSeekTo(time);
					break;
				case IPTVPlayer.LiveSeekTo:
					Log.i(LOG_TAG, "handle message: IPTVPlayer.LiveSeekTo");
//					Inform.sendMessage(Common.MSG_ADD_SHIYIIMG);
					status = playStatus.Play;
					String absStartTime = (String) msg.obj;
					nativeLiveSeekTo(absStartTime);
					break;
				case IPTVPlayer.PausePlay:
					Log.i(LOG_TAG, "handle message: IPTVPlayer.PausePlay");
					if (status == playStatus.Play) {
						status = playStatus.Pause;
						Log.v(LOG_TAG, "call pause");
						nativePause();
						if (activityPaused) {
							nativeReleaseMediaProcessor();
						}
					}
					break;
				case IPTVPlayer.Resume:
					Log.i(LOG_TAG, "handle message: IPTVPlayer.Resume");
					if (status == playStatus.Pause) {
						status = playStatus.Play;
						nativeResume();
					}
					break;
				case IPTVPlayer.StopFast:
					Log.i(LOG_TAG, "handle message: IPTVPlayer.StopFast");
					if (status == playStatus.FastForward
							|| status == playStatus.FastRewind) {
						status = playStatus.Play;
						speed = 1;

						nativeStopFast();
					}
					break;
				case IPTVPlayer.SetLive:
					Log.i(LOG_TAG, "handle message: IPTVPlayer.SetLive");
					isLive = (Boolean) msg.obj;
//					Log.v(LOG_TAG, Utils._FUNC_() + "setLive: " + isLive);
					nativeSetLive(isLive);
//					Inform.sendMessage(Common.MSG_SetLive_EpgMpde);
					break;
				case IPTVPlayer.SetProgramUrl:
					// String uri = (String)msg.obj;
					programUrl = (String) msg.obj;
					Log.i(LOG_TAG, "handle message: IPTVPlayer.SetProgramUrl > programUrl = " +programUrl);
					// nativeSetProgramURL(uri);
					break;
				case IPTVPlayer.TimeShiftPause:
					// 这样做的问题是,如果RTP流过来的比较慢, 就会出现黑屏
					Log.i(LOG_TAG, "handle message: IPTVPlayer.timeshiftpause");
					TimerTask ts = new TimerTask() {
						public void run() {
							pause();
						}
					};
					Timer timer = new Timer();
					timer.schedule(ts, 2000);
					break;
				case IPTVPlayer.INIT:
					nativeReGetMediaProcessor();
					break;
				case IPTVPlayer.RELEASE:
					nativeReleaseMediaProcessor();
					break;
				case SET_SURFACE:
					nativeSetVideoSurface((Surface) msg.obj);
					break;
				case SET_SRC_WH:
					nativeSetScrHW(msg.arg1, msg.arg2);
					break;
				case SET_VIDEO_LOC: {
					Bundle bundle = msg.getData();
					nativeSetVideoWindow(bundle.getInt("x"),
							bundle.getInt("y"), bundle.getInt("w"),
							bundle.getInt("h"));
				}
					break;
				case SET_AUDIO_BL:
					nativeSetAudioBalance(msg.arg1);
					break;
				case UPDATE_SURFACE_MTK: {
					SurfaceHolder holder = (SurfaceHolder) msg.obj;
					Canvas canvas = holder.lockCanvas();
					holder.unlockCanvasAndPost(canvas);
					break;
				}
				}
			}
		};
	}

	// set live or not
	public void setLive(boolean _isLive) {
		Message msg = new Message();
		msg.what = IPTVPlayer.SetLive;
		msg.obj = _isLive;
		playerHandler.sendMessage(msg);
	}

	// set screen width and height
	public void setScrHW(int width, int height) {
		scrWidth = width;
		scrHeight = height;
		Message msg = playerHandler.obtainMessage(SET_SRC_WH, width, height);
		msg.sendToTarget();
	}

	public void updateSurface(SurfaceHolder holder) {
		Message msg = playerHandler.obtainMessage(UPDATE_SURFACE_MTK, holder);
		msg.sendToTarget();
	}

	// play status control
	public void start() {
//		Log.i(LOG_TAG, Utils._FUNC_() + "call");
/*		if (!IPTVConfig.canPlayVideo)
			return;*/
		playerHandler.sendEmptyMessage(IPTVPlayer.StartPlay);
//		IPTVConfig.VideoRetryTime = 0;
	}

	public void stop() {
//		Log.i(LOG_TAG, Utils._FUNC_() + "call");
/*		if (!IPTVConfig.canPlayVideo)
			return;*/
		playerHandler.sendEmptyMessage(IPTVPlayer.StopPlay);
	}

	public void pause() {
//		Log.i(LOG_TAG, Utils._FUNC_() + "call");
		playerHandler.sendEmptyMessage(IPTVPlayer.PausePlay);
	}

	public void resume() {
//		Log.i(LOG_TAG, Utils._FUNC_() + "call");
		playerHandler.sendEmptyMessage(IPTVPlayer.Resume);
	}

	/**
	 * public void fastForward(int _speed) { Log.i(LOG_TAG, Utils._FUNC_() +
	 * "call speed:" + speed);
	 * playerHandler.sendEmptyMessage(IPTVPlayer.FastForward); }
	 * 
	 * public void fastRewind(int _speed) { Log.i(LOG_TAG, Utils._FUNC_() +
	 * "call speed:" + speed);
	 * playerHandler.sendEmptyMessage(IPTVPlayer.FastRewind); }
	 * 
	 * public void stopFast() { Log.i(LOG_TAG, Utils._FUNC_() + "call");
	 * playerHandler.sendEmptyMessage(IPTVPlayer.StopFast); }
	 */
	public void fastForward(int _speed) {
//		Log.i(LOG_TAG, Utils._FUNC_() + "call speed:" + speed);
		// playerHandler.sendEmptyMessage(Common.FastForward);
		status = playStatus.FastForward;
		if (speed < 64 && speed > 0) {
			speed = speed * 2;
		} else {
			speed = 2;
		}
		nativeFastForward(speed);
	}

	public void fastRewind(int _speed) {
//		Log.i(LOG_TAG, Utils._FUNC_() + "call speed:" + speed);
		// playerHandler.sendEmptyMessage(Common.FastRewind);
		/** * 如果是直播，首先显示加载效果图片 */
		if (isLive) {
//			Inform.sendMessage(Common.MSG_ADD_SHIYIIMG);
		}
		/**
		 * * 如果当前状态为播放或者快退，则更改状态为快退 * 并且在播放速度大于-64的情况下，每次点击速度乘以-2 * *
		 * 如果当前状态为快进，则更改状态为快退 * 并且播放速度为正常的-2倍
		 */
		status = playStatus.FastRewind;
		if (speed > -64 && speed < 0) {
			speed = Math.abs(speed) * -2;
		} else {
			speed = -2;
		}
		nativeFastRewind(speed);
	}

	public void stopFast() {
//		Log.i(LOG_TAG, Utils._FUNC_() + "call");
		// playerHandler.sendEmptyMessage(Common.StopFast);
		if (status == playStatus.FastForward || status == playStatus.FastRewind) {
			status = playStatus.Play;
			speed = 1;
			nativeStopFast();
		}
	}

	public void prepare() {
		// nativePrepare();
	}

	public void seekTo(long l) {
//		Log.i(LOG_TAG, Utils._FUNC_() + "call seekTo: " + l);
		Message msg = new Message();
		msg.what = IPTVPlayer.SeekTo;
		Long time = l;
		msg.obj = time;
		playerHandler.sendMessage(msg);
	}

	// coalber
	public void liveSeekTo(String absStartTime) {
//		Log.i(LOG_TAG, Utils._FUNC_() + "call seekTo: " + absStartTime);
		Message msg = new Message();
		msg.what = IPTVPlayer.LiveSeekTo;
		msg.obj = absStartTime;
		playerHandler.sendMessage(msg);
	}

	// set parameter -- java
	public void setProgramURL(String uri) {
		if (uri != null && uri.length() != 0) {
			Message msg = new Message();
			msg.what = IPTVPlayer.SetProgramUrl;
			msg.obj = uri;
			playerHandler.sendMessage(msg);
		}
	}

	public void timeShiftPause() {
		playerHandler.sendEmptyMessage(IPTVPlayer.TimeShiftPause);
	}

	public  void setSurface(Surface surface) {
		Message msg = playerHandler.obtainMessage(SET_SURFACE, surface);
		msg.sendToTarget();
	}

	public void setVideoWindow(int x, int y, int width, int height) {
		Message msg = playerHandler.obtainMessage(SET_VIDEO_LOC);
		Bundle bundle = new Bundle();
		bundle.putInt("x", x);
		bundle.putInt("y", y);
		bundle.putInt("w", width);
		bundle.putInt("h", height);
		msg.setData(bundle);
		msg.sendToTarget();
	}

	public void setAudioBalance(int audioChannel) {
		Message msg = playerHandler
				.obtainMessage(SET_AUDIO_BL, audioChannel, 0);
		msg.sendToTarget();
	}

	public void setVolume(int mp_volume) {
		nativeSetVolume(mp_volume);
	}

	public static void setActivityPaused(boolean paused) {
		activityPaused = paused;
	}

	public static boolean isActivityPaused() {
		return activityPaused;
	}

	// get info -- java
	public static boolean getIsLive() {
//		Log.v(LOG_TAG, Utils._FUNC_() + "getIsLive: " + isLive);
		return isLive;
	}

	public boolean isPlaying() {
		// || status == playStatus.beforePlay
		if (status == playStatus.Stop)
			return false;
		return true;
	}

	public int getSpeed() {
		return speed;
	}

	// get the width of original video
	public int getSystemWidth() {
//		return nativeGetSystemHW(false);
		return 1920;
	}

	// get the height of original video
	public int getSystemHeight() {
//		return nativeGetSystemHW(true);
		return 1080;
	}

	// get the length
	public int getDuration() {
		Log.v(LOG_TAG, "Hai-- getDuration " + nativeGetVideoLength());
		return (int) nativeGetVideoLength();
	}

	public int getVolume() {
		return nativeGetVolume();
	}

	public int getCurrentPlayTime() {
		return (int) nativeGetCurrentPosition();
	}

	public int getAudioBalance() {
		return nativeGetAudioBalance();
	}

	/*
	 * interface definition of a callback to be invoked indicating buffering
	 * status of a media resource being streamed over the network.
	 */

	public interface OnBufferingUpdateListener {
		/*
		 * called to update status in buffering a media stream received through
		 * progressive HTTP download. The received buffering percentage
		 * indicates how much of the content has been buffered or played. For
		 * example a buffering update of 80 percent when half the content has
		 * already been played indicates that the next 30 percent of the content
		 * to play has been buffered.
		 */
		public void onBufferingUpdate(IPTVPlayer mp, int percent);
	}

	/*
	 * interface definition of a callback to be invoked indicating the
	 * completion of a seek operation
	 */
	public interface OnSeekCompleteListener {
		/*
		 * Called to indicate the completion of a seek operation
		 * 
		 * @param mp the IPTVPlayer that issued the seek operation
		 */
		public void onSeekComplete(IPTVPlayer mp);
	}

	/*
	 * interface definition of a callback to be invoked when the video size is
	 * first known or updated
	 */
	public interface OnVideoSizeChangedListener {
		/*
		 * called to indicate the video size
		 * 
		 * @param mp the IPTVPlaer associated with this callback
		 * 
		 * @param width the width of the video
		 * 
		 * @param height the height of the vidio
		 */
		public void onVideoSizeChanged(IPTVPlayer mp, int width, int height);
	}

	/*
	 * interface definition of a callback to be invoked when there has been an
	 * error during an asynchronous operation (other errors will throw
	 * exceptions at method call time)
	 */
	public interface OnErrorListener {
		/*
		 * Called to indicate an error
		 * 
		 * @param mp the IPTVPlayer the error pertains to
		 * 
		 * @param what the type of error that has occurred:
		 * 
		 * @pamam extra an extra code, specific to the error. Typically
		 * implementation dependant.
		 * 
		 * @return True if the method handled the error, false if it didn't.
		 * Returning false, or not having an OnErrorListener at all, will cause
		 * the OnCompletionListener to be called.
		 */
		public boolean onError(IPTVPlayer mp, int what, int extra);
	}

	/*
	 * interface definition for a callback to be invoked when the media source
	 * is ready for playback.
	 */
	public interface OnPreparedListener {
		/*
		 * called when the media file is ready for playback
		 * 
		 * @param mp the IPTVPlayer that is ready for playback
		 */
		public void onPrepared(IPTVPlayer mp);
	}

	/*
	 * interface definition of a callback to be invoked to communicate some info
	 * and/or warning about the media or its playback
	 */
	public interface OnInfoListener {
		/*
		 * called to indicate an info or a warning
		 * 
		 * @param mp the IPTVPlayer the info pertains to.
		 * 
		 * @param what the type of info or warning
		 * 
		 * @param extra an extra code, specific to the info, Typically
		 * implementation dependant.
		 * 
		 * @return true if the method handled the info, false if it didn't.
		 * Returning false, or not having an OnErrorListener at all, will cause
		 * the info to be discarded.
		 */
		public boolean onInfo(IPTVPlayer mp, int what, int extra);
	}

	/*
	 * interface definition for a callback to be invoked when playback of a
	 * media source has completed.
	 */
	public interface OnCompletionListener {
		/*
		 * called when the end of a media source is reached during playback.
		 * 
		 * @param mp the IPTVPlayer that reached the end of the file
		 */
		public void onCompletion(IPTVPlayer mp);
	}

	public void setOnBufferingUpdateListener(
			IPTVPlayer.OnBufferingUpdateListener onBufferingUpdateListener) {
	}

	public void setOnCompletionListener(
			IPTVPlayer.OnCompletionListener onCompletionListener) {
	}

	public void setOnErrorListener(IPTVPlayer.OnErrorListener onErrorListener) {
	}

	public void setOnInfoListener(IPTVPlayer.OnInfoListener onInfoListener) {
	}

	public void setOnPrepareListener(
			IPTVPlayer.OnPreparedListener onPreparedListener) {
	}

	public void setOnSeekCompleteListener(
			IPTVPlayer.OnSeekCompleteListener onSeekCompleteListener) {
	}

	public void setOnVideoSizeChangedListener(
			IPTVPlayer.OnVideoSizeChangedListener onVideoSizeChangedListener) {
	}

	// get info -- C++
	public native int nativeGetPlayMode(); // invoke processor.so

	public native int nativeGetAudioBalance(); // invoke processor.so

	public native long nativeGetCurrentPosition();

	public native void nativeSeekTo(int msec);

	public native void nativeLiveSeekTo(String time);

	public native long nativeGetVideoLength();

	public native int nativeGetSystemHW(boolean isH);

	public native int nativeGetVolume();

	// set parameter -- C++
	// public static native void nativeSetProgramURL(String uri);

	public native static void nativeSetVideoSurface(Surface surface);

	public native int nativeSetVideoWindow(int x, int y, int width, int height);

	public native boolean nativeSetAudioBalance(int nAudioBalance);

	public native boolean nativeSetVolume(int volume);

	public native static void nativeSetScrHW(int width, int height);

	// play control -- C++

	public static native void nativePrepare();

	public static native void nativePause();

	public static native void nativeResume();

	public static native void nativeStartPlay(String url);

	public static native void nativeStop();

	public static native void nativeFastForward(int speed);

	public static native void nativeFastRewind(int speed);

	public static native void nativeStopFast();

	public static native void nativeSetLive(boolean _isLive);

	public static native void nativeReleaseMediaProcessor();

	public static native boolean nativeReGetMediaProcessor();

	public void release() {
		// TODO Auto-generated method stub
		// don't know what to do
		playerHandler.sendEmptyMessage(RELEASE);
	}

	public void initialize() {
		playerHandler.sendEmptyMessage(INIT);
	}

	// get function name
	// public static String _FUNC_() {
	// StackTraceElement traceElement = ((new Exception()).getStackTrace())[1];
	// return "[" + traceElement.getClassName() + ":"
	// + traceElement.getMethodName() + "]";
	// }
}
